<!DOCTYPE html>
<!--
Copyright 2015 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel='import' href='/tracing/extras/importer/trace_code_entry.html'>

<script>
'use strict';

tr.exportTo('tr.e.importer', function() {
  // This code is a tracification of:
  // devtools/front_end/timeline/TimelineJSProfile.js
  function TraceCodeMap() {
    this.banks_ = new Map();
  }

  TraceCodeMap.prototype = {
    addEntry(addressHex, size, name, scriptId) {
      const entry = new tr.e.importer.TraceCodeEntry(
          this.getAddress_(addressHex), size, name, scriptId);

      this.addEntry_(addressHex, entry);
    },

    moveEntry(oldAddressHex, newAddressHex, size) {
      const entry = this.getBank_(oldAddressHex)
          .removeEntry(this.getAddress_(oldAddressHex));
      if (!entry) return;

      entry.address = this.getAddress_(newAddressHex);
      entry.size = size;
      this.addEntry_(newAddressHex, entry);
    },

    lookupEntry(addressHex) {
      return this.getBank_(addressHex)
          .lookupEntry(this.getAddress_(addressHex));
    },

    addEntry_(addressHex, entry) {
      // FIXME: Handle bank spanning addresses ...
      this.getBank_(addressHex).addEntry(entry);
    },

    getAddress_(addressHex) {
      // 13 hex digits === 52 bits, double mantissa fits 53 bits.
      const bankSizeHexDigits = 13;
      addressHex = addressHex.slice(2);  // cut 0x prefix.
      return parseInt(addressHex.slice(-bankSizeHexDigits), 16);
    },

    getBank_(addressHex) {
      addressHex = addressHex.slice(2);  // cut 0x prefix.

      // 13 hex digits === 52 bits, double mantissa fits 53 bits.
      const bankSizeHexDigits = 13;
      const maxHexDigits = 16;
      const bankName = addressHex.slice(-maxHexDigits, -bankSizeHexDigits);
      let bank = this.banks_.get(bankName);
      if (!bank) {
        bank = new TraceCodeBank();
        this.banks_.set(bankName, bank);
      }
      return bank;
    }
  };

  function TraceCodeBank() {
    this.entries_ = [];
  }

  TraceCodeBank.prototype = {
    removeEntry(address) {
      // findLowIndexInSortedArray returns 1 for empty. Just handle the
      // empty list and bail early.
      if (this.entries_.length === 0) return undefined;

      const index = tr.b.findLowIndexInSortedArray(
          this.entries_, function(entry) { return entry.address; }, address);
      const entry = this.entries_[index];
      if (!entry || entry.address !== address) return undefined;

      this.entries_.splice(index, 1);
      return entry;
    },

    lookupEntry(address) {
      const index = tr.b.findFirstTrueIndexInSortedArray(
          this.entries_, e => (address < e.address)) - 1;
      const entry = this.entries_[index];
      return entry &&
          address < entry.address + entry.size ? entry : undefined;
    },

    addEntry(newEntry) {
      // findLowIndexInSortedArray returns 1 for empty list. Just push the
      // new address as it's the only item.
      if (this.entries_.length === 0) {
        this.entries_.push(newEntry);
      }

      const endAddress = newEntry.address + newEntry.size;
      const lastIndex = tr.b.findLowIndexInSortedArray(
          this.entries_, function(entry) { return entry.address; }, endAddress);
      let index;
      for (index = lastIndex - 1; index >= 0; --index) {
        const entry = this.entries_[index];
        const entryEndAddress = entry.address + entry.size;
        if (entryEndAddress <= newEntry.address) break;
      }
      ++index;
      this.entries_.splice(index, lastIndex - index, newEntry);
    }
  };

  return {
    TraceCodeMap,
  };
});

</script>
